package com.yilin.platform.base_core.interceptor;

import com.google.gson.reflect.TypeToken;
import com.yilin.platform.base_core.commons.utils.CommonUtil;
import com.yilin.platform.base_core.commons.utils.JsonUtil;
import com.yilin.platform.base_core.commons.utils.StreamUtil;
import com.yilin.platform.base_core.commons.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 过滤器：请求数据重构
 */
@Component
//@WebFilter(filterName = "decryptFilter", urlPatterns = "/*")
@Order(1) //测试好像这个参数不生效，实际生效的是Filter扫描到的顺序（所以起名很重要）
public class ReqBindingFilter implements Filter {
    public static Logger logger = LoggerFactory.getLogger(ReqBindingFilter.class);
    private final static String UPLOAD = "upload";
    private final static String ContentType = "Content-Type";

    private String yilinBaseUrl;//基础服务地址

    private WebApplicationContext applicationContext;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContext servletContext = filterConfig.getServletContext();
        applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request = new ReqBindingFilterWrapper((HttpServletRequest) request);
        chain.doFilter(request, response);
    }

    //返回前台数据
    private void responseData(HttpServletRequest request, HttpServletResponse response, String retDataJson, String contentType) {
        try {
            response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Headers", "content-type, token, upload");
            response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
            response.setCharacterEncoding("UTF-8");//设置字符集为'UTF-8'
            if (contentType == null) contentType = ("text/html;charset=utf-8");
            response.setContentType(contentType);//设置格式为text/json 
            PrintWriter write = response.getWriter();
            write.write(retDataJson);
            write.flush();
            write.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 过滤器：数据重构 JSON
     */
    private static class ReqBindingFilterWrapper extends HttpServletRequestWrapper {
        private Map<String, Object> bodyMap = new HashMap<>();
        boolean original = false;

        ReqBindingFilterWrapper(HttpServletRequest request) throws IOException {
            super(request);
            ///---编码
            Charset encoding = StandardCharsets.UTF_8;
            String contentType = request.getHeader("content-type");
            if (contentType != null && contentType.contains("charset=GBK")) encoding = Charset.forName("GBK");
            ///---设置编码
            request.setCharacterEncoding(encoding.name());
            boolean isJson = contentType != null && contentType.contains("application/json");
            boolean isEncrypt = Boolean.parseBoolean(request.getHeader("isEncrypt"));
            String tokenID = request.getHeader("token");
            //第一种情况，使用表单直接上送，这种情况下，得到的bodyData是字符串
            String bodyData = request.getParameter("body");
            //第二种情况，使用"application/json"上送，这种情况得到的是JSON，需从里面找到bodyData
            if (isJson && StringUtil.isEmpty(bodyData)) {
                bodyData = StreamUtil.getBodyString(request.getInputStream(), encoding);
                if (JsonUtil.isJson(bodyData)) {
                    Map<String, Object> map = JsonUtil.jsonToObject(bodyData, new TypeToken<Map<String, Object>>() {
                    });
                    if (map != null && map.containsKey("body")) bodyData = (String) map.get("body");
                }
            }
            //这里只要是JSON就一定是加密的，或者采用前端传过来的isEncrypt值判定
            //（！值得注意的是银行传过来的值是xml格式的，并且是GBK编码，这里想办法不要拦截 走 original=true）
            if (StringUtil.isNotEmpty(bodyData) && (isJson || isEncrypt)) {
                String jsonData = bodyData;
//                String jsonData = EncryptUtils.aesDecrypt(bodyData, tokenID);
                if (JsonUtil.isJson(jsonData))
                    this.bodyMap = JsonUtil.jsonToObject(jsonData, new TypeToken<Map<String, Object>>() {
                    });
                setChildMap(bodyMap);
            } else {
                original = true;
            }
        }

        //MAP实体子类转换
        @SuppressWarnings("unchecked")
        private void setChildMap(Map<String, Object> paramMap) {
            if (paramMap == null) {
                return;
            }
            List<String> mapKeys = new ArrayList<>();
            List<String> listKeys = new ArrayList<>();
            boolean hasMap = false;
            boolean hasList = false;
            for (String paramKey : paramMap.keySet()) {
                if (paramMap.get(paramKey) == null) {
                } else if (paramMap.get(paramKey) instanceof Map) {
                    mapKeys.add(paramKey);
                    hasMap = true;
                } else if (paramMap.get(paramKey) instanceof List) {
                    listKeys.add(paramKey);
                    hasList = true;
                } else if (paramMap.get(paramKey) instanceof Double) {
                    paramMap.put(paramKey, CommonUtil.trimNumber(String.valueOf(paramMap.get(paramKey))));
                } else {
                    paramMap.put(paramKey, String.valueOf(paramMap.get(paramKey)));
                }
            }
            for (String removeKey : mapKeys) {
                Map<String, Object> childMap = (Map<String, Object>) paramMap.get(removeKey);
                for (String childKey : childMap.keySet()) {
                    paramMap.put(removeKey + "." + childKey, childMap.get(childKey));
                }
                paramMap.remove(removeKey);
            }
            for (String removeKey : listKeys) {
                List<Object> list = (List<Object>) paramMap.get(removeKey);
                for (int i = 0; i < list.size(); i++) {// "obh.list.0.sss"
                    paramMap.put(removeKey + "[" + i + "]", list.get(i));
                }
                paramMap.remove(removeKey);
            }
            if (hasMap || hasList) {
                setChildMap(paramMap);
            }
        }

        //返回所有参数名
        @SuppressWarnings("rawtypes")
        @Override
        public Enumeration getParameterNames() {
            if (original) return super.getParameterNames();
            if (bodyMap == null) return null;
            setChildMap(bodyMap);
            Vector<String> names = new Vector<>(this.bodyMap.keySet());
            return names.elements();
        }

        //获取所有参数值
        @Override
        public String[] getParameterValues(String name) {
            if (original) return super.getParameterValues(name);
            if (bodyMap == null || bodyMap.get(name) == null || StringUtil.isEmpty(bodyMap.get(name).toString())) {
                return null;
            }
            String data = bodyMap.get(name).toString();
            return new String[]{data};
        }

    }
}
